use core::time;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::ptr::addr_of_mut;
use std::sync::Arc;
use time::Duration;
use serde::Serialize;
use crate::http_server::{ContentType, HttpMethod, HttpRequest, HttpResponse, HttpStatus};
use crate::log::Logger;


/// # 服务器对象
/// 存储路由映射


pub struct Server {
    thread_num: usize,
    routers: HashMap<String, RequestHandler>,
    static_res_path: String,
    static_res_handler: fn(HttpContext, &str, &str),
    static_root_path: String,
    read_timeout: u64,
    write_timeout: u64,
}

#[derive(Debug)]
pub struct HttpContext<'a> {
    request: HttpRequest,
    response: HttpResponse,
    stream: &'a mut TcpStream,
}


pub fn send_json<T: Serialize>(ctx: &mut HttpContext, obj: &T, status: HttpStatus) -> Result<usize, String> {
    let s = serde_json::to_string(&obj);
    if let Ok(res) = s {
        ctx.response.set_content_type(ContentType::ApplicationJson);
        ctx.response.write(res.as_bytes().to_owned());
        ctx.response.send(ctx.stream, status);
        Result::Ok(res.len())
    } else {
        Result::Err(String::from("send failed..."))
    }
}

impl<'a, 'b: 'a> HttpContext<'a> {
    pub fn new(request: HttpRequest, response: HttpResponse, stream: &'b mut TcpStream) -> Self {
        HttpContext {
            request,
            response,
            stream,
        }
    }

    pub fn get_request(&mut self) -> &mut HttpRequest {
        &mut self.request
    }

    pub fn get_response(&mut self) -> &mut HttpResponse {
        &mut self.response
    }

    pub fn get_stream(&mut self) -> &mut TcpStream {
        &mut self.stream
    }

    pub fn send_response(&self, status: HttpStatus) {
        self.response.send(self.stream, status);
    }
}



pub struct RequestHandler {
    accept_method: HttpMethod,
    handle: Box<dyn Fn(HttpContext) + Send + Sync + 'static>,
}

fn null_handler(_ctx: HttpContext, _root_path: &str, _res_path: &str) {}

impl Default for Server {
    fn default() -> Self {
        Server {
            thread_num: 1,
            routers: HashMap::new(),
            static_res_path: Default::default(),
            static_res_handler: null_handler,
            static_root_path: Default::default(),
            read_timeout: 60,
            write_timeout: 130,
        }
    }
}

fn hand_request(server: Arc<Box<Server>>, mut stream: TcpStream) {
    stream.set_read_timeout(Some(Duration::from_secs(server.read_timeout))).unwrap();
    stream.set_write_timeout(Some(Duration::from_secs(server.write_timeout))).unwrap();
    loop {
        let mut request = HttpRequest::new(&mut stream);
        let request = match request {
            Ok(req) => req,
            Err(_) => {
                Logger::info("There is a link interruption here");
                return;
            }
        };
        let uri = request.get_uri();
        if let Option::Some(handler) = server.routers.get(uri) {
            let method = match handler.accept_method {
                HttpMethod::Get => "GET",
                HttpMethod::Post => "POST",
                HttpMethod::Delete => "DELETE",
                HttpMethod::UNKNOWN => ""
            };
            let pro_method = match request.get_method() {
                HttpMethod::Get => "GET",
                HttpMethod::Post => "POST",
                HttpMethod::Delete => "DELETE",
                HttpMethod::UNKNOWN => ""
            };
            if method.to_string() != pro_method.to_string() {
                Logger::warn(format!("patch error: expect {} method, but {}", method, pro_method).as_str());
                return;
            }
            let ctx = HttpContext::new(request, HttpResponse::new(), &mut stream);
            (handler.handle)(ctx)
        } else {
            if uri.as_str().starts_with(server.static_res_path.as_str()) {
                if let Option::Some(_handler) = server.routers.get("/_static_") {
                    let ctx = HttpContext::new(request, HttpResponse::new(), &mut stream);
                    (server.static_res_handler)(ctx, server.static_root_path.as_str(), server.static_res_path.as_str());
                } else {
                    Logger::warn(format!("patch error: the request static file [{}] is not exits!", uri).as_str());
                }
            } else {
                Logger::warn(format!("patch error: the request path [{}] is not exits!", uri).as_str());
                return;
            }
        }
    }
}

pub fn run(server: Server, addr: &str) {
    Logger::info("Http Server start running...");
    Logger::info(format!("The address bound to the server is: {}", addr).as_str());
    Logger::info(format!("There are {} threads running in the thread pool", server.thread_num).as_str());
    Logger::info(format!("Server has {} handler", server.routers.len()).as_str());

    let listener = TcpListener::bind(addr).expect("bind failed...");

    Logger::info("Start listening to port");
    let pool = crate::thread_pool::ThreadPool::new(server.thread_num);
    let server_arc = Arc::new(Box::new(server));
    Logger::info("Thread pool successfully started");
    for s in listener.incoming() {
        let stream = s.unwrap();
        let server_clone = Arc::clone(&server_arc);
        pool.execute(move || {
            hand_request(server_clone, stream);
        })
    }
}

impl Server {
    pub fn new(thread_num: usize) -> Self {
        if thread_num == 0 {
            Logger::err("Thread num is illegal");
        }
        Server {
            thread_num,
            routers: HashMap::new(),
            static_res_path: Default::default(),
            static_res_handler: null_handler,
            static_root_path: Default::default(),
            read_timeout: 60,
            write_timeout: 120
        }
    }

    pub fn set_timeout_sec(&mut self, timeout: u64) {
        self.read_timeout = timeout
    }

    pub fn set_timeout_minutes(&mut self, timeout: u64) {
        self.read_timeout = timeout * 60
    }

    pub fn set_write_timeout_sec(&mut self, timeout: u64) {
        self.write_timeout = timeout
    }

    pub fn set_write_timeout_minutes(&mut self, timeout: u64) {
        self.write_timeout = timeout * 60
    }

    pub fn register_handler(&mut self, method: HttpMethod, path: &str, handle: Box<dyn Fn(HttpContext) + Send + Sync + 'static>) -> &mut Self {
        self.routers.insert(path.to_string(), RequestHandler {
            accept_method: method,
            handle
        });
        self
    }

    fn default_handler(_ctx: HttpContext) {}
    pub fn register_static_resource(&mut self, root_path: &str, map_path: &str) -> &mut Self {
        fn static_resource_handle(mut ctx: HttpContext, root_path: &str, map_path: &str) {
            let uri = ctx.request.get_uri().as_str();
            let file_path = &uri[map_path.len()..];


            let content_type = if file_path.ends_with(".jpg")
                || file_path.ends_with(".jpeg")
                || file_path.ends_with(".png")
                || file_path.ends_with(".ico") {
                Result::Ok(ContentType::ImageJpeg)
            } else if file_path.ends_with(".html") {
                Result::Ok(ContentType::TextHtml)
            } else if file_path.ends_with(".css") {
                Result::Ok(ContentType::TextCss)
            } else if file_path.ends_with(".js") {
                Result::Ok(ContentType::TextJavascript)
            } else {
                Logger::err("request a error static file");
                Result::Err("request a error static file")
            };

            if let Result::Ok(c_type) = content_type {
                if let Ok(mut file) = File::open(root_path.to_owned() + file_path) {
                    let mut buf = Vec::new();
                    if let Ok(_len) = file.read_to_end(&mut buf) {
                        ctx.response.set_content_type(c_type);
                        ctx.response.write(buf);
                        ctx.send_response(HttpStatus::OK);
                    } else {
                        Logger::err(format!("can't load the file [{}]", root_path).as_str());
                    }
                } else {
                    Logger::err(format!("file [{}] is not found", root_path).as_str());
                }
            }
        }
        self.routers.insert("/_static_".to_string(), RequestHandler {
            accept_method: HttpMethod::Get,
            handle: Box::new(|_http_context| {}),
        });
        self.static_res_path = map_path.to_string();
        self.static_res_handler = static_resource_handle;
        self.static_root_path = root_path.to_string();
        self
    }
}